package com.xhs.open.api.impl;

import com.google.gson.JsonObject;
import com.xhs.common.error.XhsError;
import com.xhs.common.error.XhsErrorException;
import com.xhs.common.error.XhsRuntimeException;
import com.xhs.open.api.XhsOpenComponentService;
import com.xhs.open.api.XhsOpenConfigStorage;
import com.xhs.open.api.XhsOpenMaService;
import com.xhs.open.api.XhsOpenService;
import com.xhs.open.bean.XhsOpenAuthorizerAccessToken;
import com.xhs.open.util.crypto.XhsOpenSignature;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * 第三方平台服务实现类
 */
@Slf4j
@AllArgsConstructor
public class XhsOpenComponentServiceImpl implements XhsOpenComponentService {

  private static final Map<String, XhsOpenMaService> WX_OPEN_MA_SERVICE_MAP = new ConcurrentHashMap<>();

  private final XhsOpenService wxOpenService;

  @Override
  public XhsOpenMaService getWxMaServiceByAppid(String appId) {
    XhsOpenMaService wxOpenMaService = WX_OPEN_MA_SERVICE_MAP.get(appId);
    if (wxOpenMaService == null) {
      synchronized (WX_OPEN_MA_SERVICE_MAP) {
        wxOpenMaService = WX_OPEN_MA_SERVICE_MAP.get(appId);
        if (wxOpenMaService == null) {
          XhsOpenConfigStorage storage = this.getWxOpenConfigStorage();
          wxOpenMaService = new XhsOpenMaServiceImpl(this, appId, storage.getWxMaConfig(appId));
          // 配置重试次数和重试间隔
          wxOpenMaService.setMaxRetryTimes(storage.getMaxRetryTimes());
          wxOpenMaService.setRetrySleepMillis(storage.getRetrySleepMillis());
          WX_OPEN_MA_SERVICE_MAP.put(appId, wxOpenMaService);
        }
      }
    }
    return wxOpenMaService;
  }

  public XhsOpenService getWxOpenService() {
    return wxOpenService;
  }

  @Override
  public XhsOpenConfigStorage getWxOpenConfigStorage() {
    return wxOpenService.getWxOpenConfigStorage();
  }

  @Override
  public boolean checkSignature(String timestamp, String nonce, String encrypt, String msgSignature) {
    try {
      return XhsOpenSignature.getSignature(encrypt, wxOpenService.getWxOpenConfigStorage().getComponentToken(), timestamp, nonce)
              .equals(msgSignature);
    } catch (Exception e) {
      log.error("Checking signature failed, and the reason is :", e);
      return false;
    }
  }

  @Override
  public String getComponentAccessToken(boolean forceRefresh) throws XhsErrorException {
//    final WxOpenConfigStorage config = this.getWxOpenConfigStorage();
//    if (!config.isComponentAccessTokenExpired() && !forceRefresh) {
//      return config.getComponentAccessToken();
//    }
//    Lock lock = config.getComponentAccessTokenLock();
//    lock.lock();
//    try {
//      if (!config.isComponentAccessTokenExpired() && !forceRefresh) {
//        return config.getComponentAccessToken();
//      }
//      JsonObject jsonObject = new JsonObject();
//      jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
//      jsonObject.addProperty("component_appsecret", getWxOpenConfigStorage().getComponentAppSecret());
//      jsonObject.addProperty("component_verify_ticket", getWxOpenConfigStorage().getComponentVerifyTicket());
//
//      String responseContent = this.getWxOpenService().post(API_COMPONENT_TOKEN_URL, jsonObject.toString());
//      WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent);
//      config.updateComponentAccessToken(componentAccessToken);
//      return config.getComponentAccessToken();
//    } finally {
//      lock.unlock();
//    }
    return "";
  }

  @Override
  public String post(String uri, String postData) throws XhsErrorException {
    return post(uri, postData, "component_access_token");
  }

  @Override
  public String post(String uri, String postData, String accessTokenKey) throws XhsErrorException {
//    String componentAccessToken = getComponentAccessToken(false);
//    String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + accessTokenKey + "=" + componentAccessToken;
//    try {
//      return getWxOpenService().post(uriWithComponentAccessToken, postData);
//    } catch (WxErrorException e) {
//      WxError error = e.getError();
//      if (WxConsts.ACCESS_TOKEN_ERROR_CODES.contains(error.getErrorCode())) {
//        // 强制设置access token过期，这样在下一次请求里就会刷新access token
//        Lock lock = this.getWxOpenConfigStorage().getComponentAccessTokenLock();
//        lock.lock();
//        try {
//          if (StringUtils.equals(componentAccessToken, this.getWxOpenConfigStorage().getComponentAccessToken())) {
//            this.getWxOpenConfigStorage().expireComponentAccessToken();
//          }
//        } catch (Exception ex) {
//          this.getWxOpenConfigStorage().expireComponentAccessToken();
//        } finally {
//          lock.unlock();
//        }
//
//        if (this.getWxOpenConfigStorage().autoRefreshToken()) {
//          log.warn("即将重新获取新的access_token，错误代码：{}，错误信息：{}", error.getErrorCode(), error.getErrorMsg());
//          return this.post(uri, postData, accessTokenKey);
//        }
//      }
//      if (error.getErrorCode() != 0) {
//        throw new WxErrorException(error, e);
//      }
//      return null;
//    }
    return null;
  }

  @Override
  public String post(String uri, String postData, String accessTokenKey, String accessToken) throws XhsErrorException {
    String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + accessTokenKey + "=" + accessToken;
    try {
      return getWxOpenService().post(uriWithComponentAccessToken, postData);
    } catch (XhsErrorException e) {
      XhsError error = e.getError();
      if (error.getCode() != 0) {
        throw new XhsErrorException(error, e);
      }
      return error.getMsg();
    }
  }

  @Override
  public String get(String uri) throws XhsErrorException {
    return get(uri, "component_access_token");
  }

  @Override
  public String get(String uri, String accessTokenKey) throws XhsErrorException {
//    String componentAccessToken = getComponentAccessToken(false);
//    String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + accessTokenKey + "=" + componentAccessToken;
//    try {
//      return getWxOpenService().get(uriWithComponentAccessToken, null);
//    } catch (WxErrorException e) {
//      WxError error = e.getError();
//      if (WxConsts.ACCESS_TOKEN_ERROR_CODES.contains(error.getErrorCode())) {
//        // 强制设置wxMpConfigStorage它的access token过期了，这样在下一次请求里就会刷新access token
//        Lock lock = this.getWxOpenConfigStorage().getComponentAccessTokenLock();
//        lock.lock();
//        try {
//          if (StringUtils.equals(componentAccessToken, this.getWxOpenConfigStorage().getComponentAccessToken())) {
//            this.getWxOpenConfigStorage().expireComponentAccessToken();
//          }
//        } catch (Exception ex) {
//          this.getWxOpenConfigStorage().expireComponentAccessToken();
//        } finally {
//          lock.unlock();
//        }
//        if (this.getWxOpenConfigStorage().autoRefreshToken()) {
//          log.warn("即将重新获取新的access_token，错误代码：{}，错误信息：{}", error.getErrorCode(), error.getErrorMsg());
//          return this.get(uri, accessTokenKey);
//        }
//      }
//      if (error.getErrorCode() != 0) {
//        throw new WxErrorException(error, e);
//      }
      return null;
    }

  @Override
  public String getAuthorizerAccessToken(String appId, boolean forceRefresh) throws XhsErrorException {

    XhsOpenConfigStorage config = getWxOpenConfigStorage();
    if (!config.isAuthorizerAccessTokenExpired(appId) && !forceRefresh) {
      return config.getAuthorizerAccessToken(appId);
    }
    Lock lock = config.getWxMaConfig(appId).getAccessTokenLock();
    boolean locked = false;
    try {
      do {
        locked = lock.tryLock(100, TimeUnit.MILLISECONDS);
        if (!forceRefresh && !config.isAuthorizerAccessTokenExpired(appId)) {
          return config.getAuthorizerAccessToken(appId);
        }
      } while (!locked);

      JsonObject jsonObject = new JsonObject();
//      jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
      jsonObject.addProperty("authorizer_appid", appId);
      jsonObject.addProperty("authorizer_refresh_token", getWxOpenConfigStorage().getAuthorizerRefreshToken(appId));
      String responseContent = post(API_AUTHORIZER_TOKEN_URL, jsonObject.toString());

      XhsOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = XhsOpenAuthorizerAccessToken.fromJson(responseContent);
      config.updateAuthorizerAccessToken(appId, wxOpenAuthorizerAccessToken);
      config.updateAuthorizerRefreshToken(appId, wxOpenAuthorizerAccessToken.getAuthorizerRefreshToken());
      return config.getAuthorizerAccessToken(appId);
    } catch (InterruptedException e) {
      throw new XhsRuntimeException(e);
    } finally {
      if (locked) {
        lock.unlock();
      }
    }
  }
}
